package run.iget.admin.system.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.jwt.JWT;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import run.iget.admin.system.bean.AdministratorDTO;
import run.iget.admin.system.bean.ResetPasswordDTO;
import run.iget.admin.system.bean.req.AdministratorReq;
import run.iget.admin.system.config.AdminSystemProperties;
import run.iget.admin.system.convert.AdministratorConvert;
import run.iget.admin.system.entity.Administrator;
import run.iget.admin.system.entity.Permission;
import run.iget.admin.system.entity.table.AdministratorTableDef;
import run.iget.admin.system.enums.SystemExceptionEnum;
import run.iget.admin.system.mapper.AdministratorMapper;
import run.iget.admin.system.service.AdministratorDepartmentService;
import run.iget.admin.system.service.AdministratorRoleService;
import run.iget.admin.system.service.AdministratorService;
import run.iget.admin.system.service.PermissionService;
import run.iget.framework.common.enums.YesOrNoEnum;
import run.iget.framework.common.req.PageReq;
import run.iget.framework.common.util.ExceptionThrowUtils;
import run.iget.framework.common.util.PasswordUtils;
import run.iget.framework.mail.service.MailService;
import run.iget.security.bean.SafeAuthUser;
import run.iget.security.req.LoginReq;
import run.iget.security.service.UserAuthService;

import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * <p>
 * 系统管理员表 服务实现类
 * </p>
 *
 * @author 大周
 * @since 2022-02-07
 */
@Service
@RequiredArgsConstructor
public class AdministratorServiceImpl extends ServiceImpl<AdministratorMapper, Administrator>
        implements AdministratorService, UserAuthService {

    private final PermissionService permissionService;
    private final MailService mailService;
    private final AdminSystemProperties adminSystemProperties;
    private final AdministratorRoleService administratorRoleService;
    private final AdministratorDepartmentService administratorDepartmentService;

    @Override
    public SafeAuthUser login(LoginReq loginReq) {
        // 根据账号查询用户
        Administrator administrator = this.mapper
                .selectOneByCondition(AdministratorTableDef.ADMINISTRATOR.ACCOUNT.eq(loginReq.getAccount()));
        ExceptionThrowUtils.ofNull(administrator, SystemExceptionEnum.ACCOUNT_OR_PASSWORD_ERROR);

        // 校验密码
        boolean matches = PasswordUtils.matches(administrator.getPassword(), loginReq.getPassword(),
                administrator.getSalt());
        ExceptionThrowUtils.ofFalse(matches, SystemExceptionEnum.ACCOUNT_OR_PASSWORD_ERROR);

        // 查询账户对应的角色集合
        List<Long> roleIdsByUserId = administratorRoleService.getRoleIdsByUserId(administrator.getId());
        List<Long> departmentIdsByUserId = administratorDepartmentService.getDepartmentIdsByUserId(administrator.getId());

        // 根据用户id查询权限集合
        List<Permission> permissionList = permissionService.listByRoleIds(roleIdsByUserId);
        ExceptionThrowUtils.ofEmpty(permissionList, SystemExceptionEnum.ACCOUNT_NO_PERMISSION);
        List<Permission> permissionTree = permissionService.buildMenus(permissionList);

        // 生成登录后的对象
        SafeAuthUser safeAuthUser = new SafeAuthUser();
        safeAuthUser.setId(administrator.getId());
        safeAuthUser.setTerminal(loginReq.getTerminal());
        safeAuthUser.setRoles(Objects.isNull(roleIdsByUserId) ? new HashSet<>() : new HashSet<>(roleIdsByUserId));
        safeAuthUser.setDepartments(Objects.isNull(departmentIdsByUserId) ? new HashSet<>() : new HashSet<>(departmentIdsByUserId));
        safeAuthUser.setUsername(administrator.getNickname());
        safeAuthUser.setSafeSalt(administrator.getSalt());
        safeAuthUser.setSafeValue(administrator.getPassword());
        safeAuthUser.setMenus(permissionTree);
        safeAuthUser.setPermissions(permissionList.stream().map(Permission::getUri).collect(Collectors.toSet()));
        return safeAuthUser;
    }

    @Transactional
    @Override
    public void saveOrUpdate(AdministratorDTO administrator) {
        if (administrator.getId() == null) {
            administrator.setSalt(IdUtil.fastSimpleUUID());
            administrator.setPassword(PasswordUtils.md5(administrator.getPassword(), administrator.getSalt()));
            this.save(administrator);
        } else {
            administrator.setSalt(null);
            administrator.setPassword(null);
            this.updateById(administrator);
        }
        administratorRoleService.relationRole(administrator.getId(), administrator.getRoleIdList());
    }

    @Override
    @Transactional
    public void delete(Long adminId) {
        QueryWrapper queryWrapper = QueryWrapper.create().where(AdministratorTableDef.ADMINISTRATOR.ID.eq(adminId))
                .and(AdministratorTableDef.ADMINISTRATOR.DELETABLE.eq(YesOrNoEnum.Y.getCode()));
        boolean flag = this.remove(queryWrapper);
        if (flag) {
            administratorRoleService.relationRole(adminId, null);
            administratorDepartmentService.relationDepartment(adminId, null);
        }
    }

    @Override
    public Page<Administrator> list(PageReq<AdministratorReq> pageReq) {
        AdministratorReq queryBean = pageReq.getQueryBean();
        Page<Administrator> page = new Page<>(pageReq.getPageNumber(), pageReq.getPageSize());
        QueryWrapper queryWrapper = QueryWrapper.create();
        if (Objects.nonNull(queryBean)) {
            queryWrapper.where(AdministratorTableDef.ADMINISTRATOR.ACCOUNT.like(queryBean.getAccount()))
                    .and(AdministratorTableDef.ADMINISTRATOR.NICKNAME.like(queryBean.getNickname()))
                    .and(AdministratorTableDef.ADMINISTRATOR.PHONE.likeRight(queryBean.getPhone()))
                    .and(AdministratorTableDef.ADMINISTRATOR.EMAIL.likeRight(queryBean.getEmail()))
                    .and(AdministratorTableDef.ADMINISTRATOR.ID.eq(queryBean.getId()))
                    .and(AdministratorTableDef.ADMINISTRATOR.STATUS.eq(queryBean.getStatus()))
                    .orderBy(AdministratorTableDef.ADMINISTRATOR.CREATE_TIME.desc());
        }
        return this.page(page, queryWrapper);
    }

    @Override
    public void sendInitPasswordEmail(Administrator administrator) {
        ExceptionThrowUtils.ofTrue(StrUtil.hasBlank(administrator.getAccount(), administrator.getEmail()), "账号、邮箱不可为空");
        QueryWrapper queryWrapper = QueryWrapper.create()
                .where(AdministratorTableDef.ADMINISTRATOR.ACCOUNT.eq(administrator.getAccount()))
                .and(AdministratorTableDef.ADMINISTRATOR.EMAIL.eq(administrator.getEmail()));
        // 根据账号查询用户
        List<Administrator> list = this.list(queryWrapper);
        if (CollectionUtil.isEmpty(list)) {
            return;
        }
        Date issuedDate = new Date();
        Date expireTime = DateUtil.offsetMinute(issuedDate, adminSystemProperties.getInitPasswordTokenExpireTime());
        String token = JWT.create().setIssuedAt(issuedDate).setExpiresAt(expireTime)
                .setPayload(AdministratorTableDef.ADMINISTRATOR.ID.getName(), administrator.getId())
                .setKey(adminSystemProperties.getSecret().getBytes()).sign();
        // 生成重置密码链接
        mailService.sendInitPasswordEmail(administrator.getEmail(), adminSystemProperties.getInitPasswordUrl(token));
    }

    @Override
    public void resetPassword(ResetPasswordDTO resetPasswordDTO) {
        boolean verify = JWT.of(resetPasswordDTO.getToken()).setKey(adminSystemProperties.getSecret().getBytes())
                .validate(0);
        ExceptionThrowUtils.ofFalse(verify, SystemExceptionEnum.LINK_IS_INVALID);

        JWT jwt = JWT.of(resetPasswordDTO.getToken());
        Integer adminId = (Integer) jwt.getPayload(AdministratorTableDef.ADMINISTRATOR.ID.getName());
        ExceptionThrowUtils.ofNull(adminId, SystemExceptionEnum.LINK_IS_INVALID);

        Administrator administrator = this.getById(adminId);
        ExceptionThrowUtils.ofNull(adminId, SystemExceptionEnum.LINK_IS_INVALID);

        this.resetPassword(administrator, resetPasswordDTO.getPassword());
        this.updateById(administrator);
    }

    @Override
    public AdministratorDTO load(Long id) {
        Administrator administrator = this.getById(id);
        if (Objects.isNull(administrator)) {
            return null;
        }
        AdministratorDTO administratorDTO = AdministratorConvert.I.to(administrator);
        administratorDTO.setRoleIdList(administratorRoleService.getRoleIdsByUserId(id));
        administratorDTO.setDepartmentIdList(administratorDepartmentService.getDepartmentIdsByUserId(id));
        return administratorDTO;
    }

    /**
     * 对管理员进行密码重置
     *
     * @param administrator
     * @param password
     */
    private void resetPassword(Administrator administrator, String password) {
        String newPassword = PasswordUtils.md5(password, administrator.getSalt());
        Administrator update = new Administrator();
        update.setId(administrator.getId());
        update.setPassword(newPassword);
        this.updateById(update);
    }
}
